#!/usr/bin/env python3
# Created by han on 2016/7/4
import json
import os
import re
import socketserver
import subprocess
import sys

sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import tools


class Hftp(socketserver.BaseRequestHandler):
    def handle(self):
        home_name = self.request.recv(4096)  #用户只要连接到服务器就会传递过来一个用户家目录的名字
        # print("+++++++",home_name.decode())
        self.request.send(bytes("欢迎使用Hftp", "utf8"))
        while True:
            data = self.request.recv(4096)          #接受客户端发来的命令
            client_data = data.decode()     #将用户传过来的bypes形式转换
            # print(data,len(data))
            if len(data) == 0:                      #如果对方挂断，则执行这里
                print("---- > 挂断")
                break
            print("[%s] input: %s" % (self.client_address, client_data))   #log
            if not re.findall("^\{.*\}$",client_data):    #如果客户端传过来的不是json格式，就是执行命令，
                self.task_cmd(client_data, home_name)
                continue
            task_data = json.loads(client_data)     #如果是json肯定就是注册上传和下载
            task_type = task_data["action"]         #获取用户的动作
            if hasattr(self, "task_%s" % task_type):        #判断没有这个功能
                func = getattr(self, "task_%s" % task_type) #有就执行
                func(task_data, home_name.decode())

    def task_cmd(self, client_cmd, home_name):
        """
        执行命令
        :param client_cmd:
        :param home_name:
        :return:
        """
        cmd_data = subprocess.Popen("%s ../space/%s" % (client_cmd, home_name.decode()), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        ret = cmd_data.stdout.read()  # python3 中默认返回的就是bytes类型
        if not ret:                   #如果没有正常输出，就是命令有问题
            ret = cmd_data.stderr.read()        #把错误信息读出来
        if len(ret) == 0:  # 执行命令的结果        #如果长度为零，就为这个命令没有返回值
            ret = bytes("none", "utf8")
        ready_data = "ready|%d" % len(ret)      #开始准备传输，解决粘包问题
        print(ready_data)
        self.request.sendall(bytes(ready_data, "utf8"))     #传输这个准备数据
        client_ready = self.request.recv(4096)
        print('-----client_ready', client_ready.decode())
        if client_ready.decode().startswith("start"):
            self.request.sendall(ret)

    def task_put(self,task_data,home_name):
        home_list = home_name.split("_")
        file_name = task_data["name"].split("\\")[-1]       #获取用户名字
        file_size = task_data["size"]       #获取文件数据代销
        # abs_file_name = home_name.decode() + "/" + file_name
        abs_file_name = "../space/" + home_name + "/" + file_name
        # print("=======",abs_file_name)
        file_md5 = task_data["md5"]         #获取文件的原始md5值
        home_size = int(home_list[1])*1024*1024       # home_size 单位是 b  获取家目录大小，家目录大小单位为M，用户上传文件是需要做一个单位转换
        dir_size = tools.dir_size(home_name)          #  may_size 单位是 b       获取用户家目录当前大小
        may_size = home_size - dir_size               #家目录大小 - 已经用了的大小，就是还能上传的大小
        # print(file_size,may_size)
        if file_size > may_size:                      #如果文件大于还能上传的大小
            msg = {"status": "3","file": file_size,"max": may_size}
            msg_str = json.dumps(msg)
            self.request.sendall(bytes(msg_str, "utf8"))
            return "no_size"
        if not os.path.exists(abs_file_name):       #如果这个文件不存在
            msg = json.dumps({"status": "1","seek": 0,"server_size":0})
            self.request.sendall(bytes(msg,"utf8")) #发送确认信息
            recv_size = 0                           #初始化接受文件大小
            with open(abs_file_name,"wb") as local_file:        #新建一个要存储的文件
                while recv_size < file_size:                    #如果接受的大小小于文件的大小
                    data = self.request.recv(4096)              #那么就接收
                    recv_size += len(data)                      #将接收大小累加
                    local_file.write(data)                      #往文件中写
                    # print(data)
                    print("file_size %s , recv_size %s" % (file_size,recv_size))
            recv_md5 = tools.get_file_md5(abs_file_name)    #接收后文件的md5值
            if recv_md5 == file_md5:                        #对比两个文件的md5值
                msg = "success"
                self.request.sendall(bytes(msg, "utf8"))    #发送成功消息
                tools.color_msg(32, msg, 40)
            else:
                msg = "incomplete"
                self.request.sendall(bytes(msg, "utf8"))    #发送残缺消息
                tools.color_msg(32, msg, 40)
        else:                                               #如果已经存在这个文件
            local_md5 = tools.get_file_md5(abs_file_name)   #获取本地文件的md5
            if local_md5 == file_md5:                       #校验md5值一样
                msg = json.dumps({"status": "0", "seek":0}) #表示文件完整，不用传输
                self.request.sendall(bytes(msg,"utf8"))
            else:                                           #如果不一样
                with open(abs_file_name,"a+b") as local_file:      #以追加字节的模式打开
                    before_pos = local_file.tell()                 #找到当前文件最后的位置
                    recv_size = os.stat(abs_file_name).st_size
                    msg = json.dumps({"status": "2", "seek": before_pos, "server_size": recv_size})  #server_size : server端把现有文件的大小也传给客户端
                    self.request.sendall(bytes(msg, "utf8")) #将这个位置传给客户端
                    while recv_size < file_size:            #收到的大小小于文件大小时
                        data = self.request.recv(4096)      #循环接受
                        recv_size += len(data)              #继续加上数据的大小
                        local_file.write(data)              #追加写入文件
                        print("file_size %s , recv_size %s" % (file_size, recv_size))
                recv_md5 = tools.get_file_md5(abs_file_name)
                if recv_md5 == file_md5:
                    msg = "success"
                    self.request.sendall(bytes(msg, "utf8"))  # 发送成功消息
                    tools.color_msg(32, msg, 40)
                else:
                    msg = "incomplete"
                    self.request.sendall(bytes(msg, "utf8"))  # 发送残缺消息
                    tools.color_msg(32, msg, 40)

    def task_get(self, task_data, home_name):
        """
        客户端下载文件
        :param task_data:client 传过来的数据信息
        :param home_name: 客户端的家目录
        :return:
        """
        abs_filename = "../space/%s/%s"%(home_name, task_data["file_name"]) #获取用户家的绝对路径
        if not os.path.exists(abs_filename):        #如果这个文件本地不存在
            send_msg = {"status": "1"}                                 # 第一次发送   如果客户端收到是1 就不向服务器发消息了
            send_json = json.dumps(send_msg)
            self.request.sendall(bytes(send_json, "utf8"))
            return "no this file"
        else:                           #本地存在，就要看客户端的信息了
            file_size = os.stat(abs_filename).st_size                   #本地文件的大小
            local_md5 = tools.get_file_md5(abs_filename)                #本地文件的md5值
            client_md5 = task_data["client_md5"]
            if client_md5 == 0:                                         #代表客户端没有这个文件
                send_msg = {"status": "4", "server_size": file_size, "server_md5": local_md5}
                send_json = json.dumps(send_msg)
                self.request.sendall(bytes(send_json,"utf8"))               #第一次发送
                recv_status = self.request.recv(2048)                                        #第二次接收信息  客户端发来的确认发送信息
                if recv_status.decode() == "start":
                    tools.send_file(self, task_data["client_seek"], abs_filename, 0, file_size)             #第二次发送  发送完整数据
                elif recv_status.decode() == "stop":
                    return "client stop transmission "
            else:                   #客户端发来的md5不等于0
                if local_md5 == client_md5:             #那就是客户端有这个文件
                    msg = json.dumps({"status": "0"})
                    self.request.sendall(bytes(msg,"utf8"))                     #第一次发送
                else:                                   #如果不等就是需要续传，
                    send_msg = {"status": "2", "server_size": file_size, "server_md5": local_md5}
                    send_json = json.dumps(send_msg)
                    self.request.sendall(bytes(send_json, "utf8"))          #第一次发送
                    recv_msg = self.request.recv(1024)                   #第二次接收确认发送信息
                    recv_status = recv_msg.decode()
                    if recv_status == "start":
                        tools.send_file(self, task_data["client_seek"], abs_filename, task_data["client_size"], file_size)  #第二次发送完整信息
                    elif recv_status == "stop":
                        return "client stop transmission "

    def task_register(self, task_data, home=None):
        """
        用户注册
        :param task_data:  用户的信息，类型字典
        :return:
        """
        user_name = task_data["name"]               #获取用户名
        user_size = task_data["user_size"]          #获取用户空间大小
        home_name = user_name + "_" + user_size     #拼接家目录名字
        with open("../db/%s" % user_name,"w",encoding="utf8") as user_file:
            json.dump(task_data, user_file)
        subprocess.call("mkdir ../space/%s" % home_name, shell=True)  #创建家目录
        self.request.send(bytes("success", "utf8"))    #发送成功信息

    def task_login(self, task_data, user_name):
        local_dict = tools.read_info(user_name) #读取用户的信息
        if local_dict["passwd"] == task_data["passwd"]:     #如果用户登陆信息和本地信息相符
            msg = "1"
        else:
            msg = "0"
        self.request.sendall(bytes(msg,"utf8"))
